#include "test_timers.h"
#include "user_timers.h"
#include "os_io.h"
#include "os_mem.h"
#include "process.h"

#define NUM_TIMERS_TO_START 10
static user_timer_data_t *my_timers[NUM_TIMERS_TO_START];
static uint32_t timer_intervals[NUM_TIMERS_TO_START] = 
  { 1000, 1000, 1000, 1000, 1000, 1000, 1000, 2000, 3000, 1000 };
static uint32_t timer_periodicity[NUM_TIMERS_TO_START] = 
  { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
static uint32_t periodic_timer_hit_count = 0;
static queue_handle_t q_handle;
static uint32_t timer_mask = 0;

void expiry_handler(void *arg)
{
    uint32_t timer_id = (uint32_t)arg;
    uint32_t temp = 0;

    timer_mask = (timer_mask | (0x1u << timer_id));
    printf("Timer 0x%x expired\n", timer_id);

    switch(timer_id)
    {
        case 0:
	case 1:
	case 2:
	    // Multiple timers kept in same bucket
	    // have expired
	    break;
	
	case 3:  // cancel from queue
	case 5:  // cancel from queue
	case 7:  // cancel from bucket
	    // Here we cancel next timer (cancelled from queue)
	    temp = timer_id + 1;
	    user_timer_cancel(my_timers[temp]);
	   // printf("Timer state for 0x%x is 0x%x\n", temp, my_timers[temp]->state);
	    timer_mask = (timer_mask | (0x1u << temp));
	    break;

	case 4:  // should have got cancelled from queue
	case 6:  // should have got cancelled from queue
	case 8:  // should have got cancelled from bucket
	   // These timers were cancelled, should not expire
	   printf("Error!! Timer 0x%x expired. Should not happen\n", timer_id);
	   timer_mask |= 0x80000000;  // corrupt timer mask to generate error in final evaluation
	   break;
	
	case 9:
	    // This is periodic timer. Count 10 times, and then cancel
	    periodic_timer_hit_count++;
	    if (periodic_timer_hit_count == 10) {
	        user_timer_cancel(my_timers[timer_id]);
		enqueue(q_handle->queue_id, (uint8_t *)&timer_id, 4);  // Unblock process
	    } else if (periodic_timer_hit_count > 10) {
	        printf("Error!! Periodic timer 0x%x still not cancelled\n", timer_id);
	        timer_mask |= 0x80000000;  // corrupt timer mask to generate error in final evaluation
	    }
	    //printf("Periodic timer hit count: 0x%x\n", periodic_timer_hit_count);
	    break;
	
	
	default:
	  printf("Unknown timer expired: 0x%x\n", timer_id);
	  timer_mask |= 0x80000000;
	  break;
    }

    return;
}


void start_timers_test(void)
{
    user_timer_data_t *tim_data;
    err_t err_code;
    err_t tim_error;
    queue_msg_t queue_msg;
    uint32_t i = 0;



    err_code = create_queue((const uint8_t *)"high_prio_queue", NULL, &q_handle);
    //printf("timer_Test: Queue created with err_code: 0x%x\n", err_code);
    if (err_code != ERR_SUCCESS) {
        return;
    }

    for (i = 0; i < NUM_TIMERS_TO_START; i++) {
        my_timers[i] = (user_timer_data_t *)calloc(1, sizeof(user_timer_data_t));
	if (my_timers[i] == NULL) {
	    printf("Unable to allocate tim data 0x%x\n", i);
	    return;
	}

	tim_data = my_timers[i];
        tim_data->user_handler = expiry_handler;
        tim_data->user_handler_arg = (void *)i;
        tim_data->interval = timer_intervals[i];
        tim_data->periodic = timer_periodicity[i];
        tim_data->q_id = q_handle->queue_id;
    }
	

    for (i = 0; i < NUM_TIMERS_TO_START; i++) {

        tim_data = my_timers[i];
        tim_error = user_timer_start(tim_data);
        if (tim_error != ERR_SUCCESS) {
            printf("Error returned while starting timer: 0x%x\n", tim_error);
        }
    }
    
    dequeue(q_handle, &queue_msg);
    if (timer_mask != ((0x1u << NUM_TIMERS_TO_START) - 1)) {
      printf("ERROR!! Some timer missed. 0x%x\n", timer_mask);
    } else {
      printf("Timers test successful!!\n");
    }

    return;
}

void test_timers(void)
{
  exec_mem(start_timers_test, (const uint8_t *)"start_timers_test", 0, 10);
}


    
